#include "Scythe.h"
//#include "utilityfunctions.h"
#include "ogreSkeletonSetup.h"

#include "ETTerrainManager.h"
#include "ETTerrainInfo.h"
#include "ETBrush.h"
#include "ETSplattingManager.h"

const bool	HW		= true;
string		pathRoot	= "H:\\Devel\\#PROJECTS\\Ogre\\Proyecto_Simio\\bin\\debug\\";

//first we need an instance of our loader class
Scythe::PhysicsClass physics;
NxScene*		nWorld;
NxPhysicsSDK*	physicsSDK;
Ogre::SceneManager* scene;
//RaySceneQuery* raySceneQuery = 0;

//define our callback functions
void storeJointCallback(NxJoint* joint, const Scythe::PhysicsJoint* data, void* userData);
void storeBodyCallback(Scythe::PhysicalObject*, NxActor* body, const Scythe::PhysicsActor* data, void* userData, int skelID);
void loadModelCallback(Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, const int id, const char* 

boneName);
void loadSkeletalCallback(Scythe::PhysicalObject* ,const char*, const Scythe::PhysicsModel*, void*, Scythe::SkeletonData*, const 

Scythe::Vector3, const Scythe::Matrix);//Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, void* userData, 

const Scythe::SkeletonData* skelID, const Scythe::Vector3 position, const Scythe::Matrix rotation);
void loadLightCallback(Scythe::PhysicalObject*, const Scythe::PhysicsLight* data, const int id);

class AnEntityClass
{
public:
	Ogre::Entity*		ent;
	Ogre::SceneNode*	node;
	int				scytheID;
};


class ALightClass 
{
public:
	Ogre::Light*		light;
	Ogre::SceneNode*	node;
	int				scytheID;
};

//This is a class that represents your own entity classes (see head.h)
//an encapsulation of a whole .phs file construction.  
class PhysicsObject : public Scythe::PhysicalObject
{
public:
	PhysicsObject(){}
	~PhysicsObject() {		//use this to destroy your entities
		for (int i = 0 ; i < ents.size(); i++) {
			///render.destroy(ents[i]->ent);
			delete ents[i];
		}
		ents.clear();

		for (int i = 0 ; i < lights.size(); i++) {
			///render.destroy(lights[i]->light);
			delete lights[i] ;
		}
		lights.clear();
		//this is the root class's destroy function
		destroy(nWorld);
	}


	vector<AnEntityClass*>	ents;
	vector<ALightClass*>	lights;

	void			update();
};

std::vector<PhysicsObject*>		models;		//our list of entities



//This represents a character class in your game
//what we have also done here is made a ScytheSkeleton class, which we use to organise our bones and
//do the ragdoll calculations (see ogreSkeletonSetup.h)
//as with normal entities you need an ID value to associate with Scythe (skeletalID is in the ScytheSkeleton class)
class ACharacterClass
{
public:
	ScytheSkeleton		skeleton;
};
ACharacterClass* player;

/////////////////////////////
// for newton, we use these to create breakable joints
struct JointData
{
	bool	breakable;
	float	maxForce;

	bool	limitsOn;
	float	limits[2];
};

Ogre::Entity* loadModel(const char* name, Ogre::Vector3 pos)
{
	static int entityID = 0;
	entityID++;
	char buf[100];
	std::string s(itoa(entityID,buf,10));

	Ogre::Entity *ent1 = 0;

	ent1 = scene->createEntity( s, name );
	
	if(!ent1) {
		return 0;
	}

	ent1->setNormaliseNormals( true );
	
	Ogre::SceneNode *node1 = scene->getRootSceneNode()->createChildSceneNode();
	node1->attachObject( ent1 );

	return ent1;
}


Ogre::Light* loadLight(bool spot)
{
	using namespace Ogre;
	static int lightID = 0;
	lightID++;
	char buf[100];
	std::string s(itoa(lightID,buf,10));
	s += "light";

	Light* light = scene->createLight( s );

	if(spot)
		light->setType( Ogre::Light::LT_SPOTLIGHT );
	else
		light->setType( Ogre::Light::LT_POINT );

	light->setDiffuseColour( 1.0, 1.0, 1.0 );

	Vector3 v = Vector3(0,-1,0);
	light->setDirection( v );
	light->setSpecularColour( 1.0, 1.0, 1.0 );/**/

	SceneNode *node1 = scene->getRootSceneNode()->createChildSceneNode();
	node1->attachObject( light );

	return light;
}

//this is your init function called at level startup, this demonstrates how you load
//physics objects from file
//,RenderWindow *rwindow
//Ogre::Root *OgreRoot,
void initializePhysx(SceneManager *sceneManager ) //Ogre::Root *OgreRoot,RenderWindow *rwindow,SceneManager *sceneMgr 
{
	//---
	//First we must initialise our physics engine
	//---
	physicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
	if(!physicsSDK)	return;
	// Create a scene
	NxSceneDesc sceneDesc;
	sceneDesc.gravity				= NxVec3(0.0f, -9.81, 0.0f); //-9.81
	sceneDesc.simType			= NX_SIMULATION_SW;
	if(HW) {
		if(physicsSDK->getHWVersion() != NX_HW_VERSION_NONE) {
			sceneDesc.simType	= NX_SIMULATION_HW;
		} 
	}
	nWorld = physicsSDK->createScene(sceneDesc);
	if(!nWorld) return;

	nWorld->setTiming(1.0f/60.0f, 8);
	physicsSDK->setParameter(NX_SKIN_WIDTH, 0.0f); //0.01f
	NxMaterial*	defaultMaterial = nWorld->getMaterialFromIndex(0);
	defaultMaterial->setRestitution(0.05f);
	defaultMaterial->setStaticFriction(0.8f);
	defaultMaterial->setDynamicFriction(0.8f);

	//----------------------------------------------------------------

	//ogre = OgreRoot;
	//window = rwindow;
	scene = sceneManager;

/*
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./media/Scythe/data", "FileSystem", "General");
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"./media/Scythe/data/meshes", "FileSystem", "General");
	Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("General");
*/
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../../Media/Scythe/data", "FileSystem", "General");
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
		"../../Media/Scythe/data/meshes", "FileSystem", "General");
	Ogre::ResourceGroupManager::getSingleton().initialiseResourceGroup("General");

	//---
	//We setup the loader class with our callback functions
	//---
	physics.setBodyCreatedCallback( storeBodyCallback );
	physics.setJointCreatedCallback( storeJointCallback );
	physics.setLoadModelCallback( loadModelCallback );	//this is the important one
	physics.setLoadLightCallback( loadLightCallback );	//only needed if you want lights
	physics.setLoadSkeletalModelCallback( loadSkeletalCallback );	//only needed if you want ragdolls

	//Here we create our own physics entity classes that are
	//inherited from Scythe::PhysicalObject.  We then keep track 
	//of them, and can update them or destroy them whenever we want
	PhysicsObject* cratesObj = new PhysicsObject;
	PhysicsObject* cratesObj2 = new PhysicsObject;
	PhysicsObject* windmillObj = new PhysicsObject;
	PhysicsObject* groundObj = new PhysicsObject;
	PhysicsObject* ragdollObj = new PhysicsObject;
	PhysicsObject* ballObj = new PhysicsObject;

	//put them all in a global list
	models.push_back(cratesObj);
	models.push_back(cratesObj2);
	models.push_back(windmillObj);
	models.push_back(groundObj);
	models.push_back(ragdollObj);
	models.push_back(ballObj);

	Scythe::Vector3 position; // = mCamera->getPosition();
	position.x = 0; //700
	position.y = 0; //-4.3 //HeightFunction::getTerrainHeight(position.x, position.z) + 3;
	position.z = 0; //740

	Scythe::Vector3 camp; // = mCamera->getPosition();
	camp.x = 670; //890 700
	camp.z = 300; //700
	camp.y = 380; 

	//---
	//Once setup, this is how you load a physics file
	//---
	
	Scythe::PhysicsEntity* crates = new Scythe::PhysicsEntity;
	crates->loadFile("..\\..\\Media\\Scythe\\data\\Stack of crates.phs");
	physics.insert(cratesObj, nWorld, crates, Scythe::Vector3(4,1,-14));
	physics.insert(cratesObj2, nWorld, crates, camp);

	Scythe::PhysicsEntity* windmill = new Scythe::PhysicsEntity;
	windmill->loadFile("..\\..\\Media\\Scythe\\data\\windmillbreakable.phs");
	physics.insert(windmillObj, nWorld, windmill, camp);
		
	Scythe::PhysicsEntity* ball = new Scythe::PhysicsEntity;
	ball->loadFile("..\\..\\Media\\Scythe\\data\\ball.phs");
	physics.insert(ballObj, nWorld, ball, camp);
	

	//////////////////////////////////////////////////////////////////////////
	//			RAGDOLL
	//////////////////////////////////////////////////////////////////////////

	Scythe::PhysicsEntity* ragdoll = new Scythe::PhysicsEntity;
	ragdoll->loadFile("..\\..\\Media\\Scythe\\data\\zombie.phs");

	physics.insert(ragdollObj, nWorld, ragdoll, camp);
	player->skeleton.setRagdollToSkeleton();		//player variable was initialised in the ragdoll callback, but it doesnt 

have to


/**/
}

//Create the terrain Actor based on data gathered from ETM
void createPhysxTerrain(ET::TerrainManager *tMgr)
{
	NxHeightFieldDesc mNxHeightFieldDesc;
	
	mNxHeightFieldDesc.nbColumns = (NxU32)tMgr->getTerrainInfo().getHeight();
    mNxHeightFieldDesc.nbRows    = (NxU32)tMgr->getTerrainInfo().getWidth();
    mNxHeightFieldDesc.verticalExtent      = -1000;
    mNxHeightFieldDesc.convexEdgeThreshold = 0;

    //Allocate storage for data
    mNxHeightFieldDesc.samples      = new NxU32[mNxHeightFieldDesc.nbColumns * mNxHeightFieldDesc.nbRows];
    mNxHeightFieldDesc.sampleStride = sizeof(NxU32);

    NxU8* currentByte = (NxU8*)mNxHeightFieldDesc.samples;
    ET::TerrainInfo i = tMgr->getTerrainInfo();
    std::vector<float> heightData = i.getHeightmapData();
      
    for (NxU32 row = 0; row < mNxHeightFieldDesc.nbRows; row++)
    {
		for (NxU32 column = 0; column < mNxHeightFieldDesc.nbColumns; column++)
		{
			NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

			//Transform ETM array of floats into NxHeightFieldSample
			currentSample->height         = static_cast<NxI16>(((i.at(row,column)) * 65535) - 32768);
			currentSample->materialIndex0 = 0;
			currentSample->materialIndex1 = 0;
            
			currentSample->tessFlag = 0;
            
			currentByte += mNxHeightFieldDesc.sampleStride;
		}
    }
	NxHeightField *mHeightField;
    mHeightField = nWorld->getPhysicsSDK().createHeightField(mNxHeightFieldDesc);
	
    //Data has been copied, buffer can be deleted
    delete[] mNxHeightFieldDesc.samples;
	
    Ogre::AxisAlignedBox aab = tMgr->getTerrainInfo().getExtents(); // getDimensions();
    Ogre::Vector3 s = aab.getSize();

    NxVec3 size;
    size.x = s.x;
    size.y = s.y;
    size.z = s.z;

    NxHeightFieldShapeDesc heightFieldShapeDesc;
    heightFieldShapeDesc.heightField   = mHeightField;
    heightFieldShapeDesc.shapeFlags      = NX_SF_FEATURE_INDICES | NX_SF_VISUALIZATION;
    heightFieldShapeDesc.heightScale   = size.y / 65536.0f;
    heightFieldShapeDesc.rowScale      = size.x / NxReal(mNxHeightFieldDesc.nbRows-1);
    heightFieldShapeDesc.columnScale   = size.z / NxReal(mNxHeightFieldDesc.nbColumns-1);
    //heightFieldShapeDesc.meshFlags   = NX_MESH_SMOOTH_SPHERE_COLLISIONS;
    heightFieldShapeDesc.materialIndexHighBits = 0;
    heightFieldShapeDesc.holeMaterial = 2;
    heightFieldShapeDesc.localPose.t = NxVec3(0,0,0);
    Ogre::Vector3 offset = tMgr->getTerrainInfo().getOffset();
	heightFieldShapeDesc.localPose.t = NxVec3(offset.x,0,offset.z); // AABB1

    NxActorDesc actorDesc;
    actorDesc.shapes.pushBack(&heightFieldShapeDesc);
    actorDesc.density   = 1.0f;
    actorDesc.globalPose.t = NxVec3(0,size.y/2,0);

    nWorld->createActor(actorDesc); 

	return;
}


//your main update function, called every frame
void updatePhysx(Real time)
{
	//Ogre::WindowEventUtilities::messagePump();
	//update your physics engine
	nWorld->simulate((NxReal)time); //1.0f/40.0f
	nWorld->flushStream();
	nWorld->fetchResults(NX_RIGID_BODY_FINISHED, true);

	//we update each object in the game
	for(unsigned int i = 0; i < models.size(); i++)
	{
		models[i]->update();
	}

	//run the ragdoll calculations
	player->skeleton.updateRagdoll();
	//ogre->renderOneFrame();
}

void PhysicsObject::update()
{
	//we update each object in the game
	//we ask scythe to give us the position and rotation for each model
	//we do this by giving it the ID we got in the loadModelCallback
	for(unsigned int i = 0; i < ents.size(); i++)
	{
		//get position
		Scythe::Vector3 pos = getModelPos( ents[i]->scytheID );
		//set position
		ents[i]->node->setPosition( pos[0], pos[1], pos[2] );
		//get orientation
		Scythe::Quat rot = getModelRotQuat( ents[i]->scytheID );
		//set orientation
		ents[i]->node->setOrientation(rot.w, rot.x, rot.y, rot.z);
	}
	//same again for our lights, we can use the same functions
	for(unsigned int i = 0; i < lights.size(); i++)
	{
		//get position
		Scythe::Vector3 pos = getModelPos( lights[i]->scytheID );
		//set position
		lights[i]->node->setPosition( pos[0], pos[1], pos[2] );
		//get orientation
		Scythe::Quat rot = getModelRotQuat( lights[i]->scytheID );
		//set orientation
		lights[i]->node->setOrientation(rot.w, rot.x, rot.y, rot.z);
	}
}


///////////////////////////////////////////////
//			SCYTHE CALLBACKS
//////////////////////////////////////////////

//called when a new rigidbody is created
void storeBodyCallback(Scythe::PhysicalObject*, NxActor* body, const Scythe::PhysicsActor* data, void* userData, int skelID)
{
	//Only actors know if they are meant to be attached to a ragdolls bone or not
	//so if this actor is, here we tell the bone that it is ragdoll controlled
	if(data->boneModelID > -1)	//note: skelID only valid if this is also valid
	{
		//in this case we know it will be "player", but when you have lots of ragdolls you will
		//obviously need a way to look up the right character using the skelID
		player->skeleton.setBoneIsRag(true, data->boneName);
	}
}



//called when a new joint is created
void storeJointCallback(NxJoint* joint, const Scythe::PhysicsJoint* data, void* userData)
{

}



// when a new model needs to be created, scythe tells you here, and you load the model yourself
void loadModelCallback(Scythe::PhysicalObject* physObj, const char* modelName, const Scythe::PhysicsModel* data, const int id, const char* 

boneName)
{
	//load the model
	Ogre::Entity* ent = loadModel(modelName, toOgre(data->pos));

	//every game has some kind of class that represents an entity, or a character etc.
	//so here we create a new one and let it know its model id
	AnEntityClass* newEntity = new AnEntityClass;
	newEntity->ent			= ent;
	newEntity->node		= ent->getParentSceneNode();
	newEntity->scytheID		= id;

	//add it to our physics object class
	PhysicsObject* po = (PhysicsObject*)physObj;		//need to cast it to your own type
	po->ents.push_back( newEntity );

	//scale the model, although scaling of models should generally
	//be avoided for performance reasons, it is supported
	newEntity->node->setScale(toOgre(data->scale));
	//newEntity->node->setScale(Vector3(20.0,20.0,20.0));
	//this is a special case, where we have a model attached to a skeleton bone, but no physics body
	if(string(boneName) != "")
	{	
		player->skeleton.attachDependantModel(boneName, id);
	}
}



//When a ragdoll is loaded, this function is called.  Use it to load or designate the model and to start
//setting up your ragdoll calculations
void loadSkeletalCallback(Scythe::PhysicalObject*, const char* modelName, const Scythe::PhysicsModel* data, void* userData, 

Scythe::SkeletonData* skelID, const Scythe::Vector3 position, const Scythe::Matrix rotation)
{
	//this is your character
	player = new ACharacterClass;
	Ogre::Entity* ent = loadModel("zombie2.mesh", Ogre::Vector3(0,0,0));	//load the actual model

	ent->getParentSceneNode()->setPosition(toOgre( position ));

	//our skeleton setup function that makes a hierarchal list of all the bones (for later use)
	//we give it a pointer to the entity model, and the skeleton ID we got from scythe
	player->skeleton.setup(ent, skelID);

}



// called when scythe wants you to create a light
void loadLightCallback(Scythe::PhysicalObject* physObj, const Scythe::PhysicsLight* data, const int id)
{
	Ogre::Light* light = 0;
	//create light
	if(data->type == Scythe::LIGHT_SPOT)
		light = loadLight(true);
	else
		light = loadLight(false);

	if(!light) return;

	//store data
	ALightClass* newLight = new ALightClass;
	newLight->light = light;
	newLight->node = light->getParentSceneNode();
	newLight->scytheID = id;

	//add it to our physics object class
	PhysicsObject* po = (PhysicsObject*)physObj;		//need to cast it to your own type
	po->lights.push_back( newLight );

	//set color
	light->setDiffuseColour(Ogre::ColourValue( data->RGB[0], data->RGB[1], data->RGB[2] ));
	light->setSpecularColour(Ogre::ColourValue( data->RGB[0], data->RGB[1], data->RGB[2] ));
}

void destroyPhysx()
{
	
	//delete scene;
	
	for (int i = 0 ; i < models.size(); i++) {
		///render.destroy(ents[i]->ent);
		delete models[i];
	}
	models.clear();
    
	delete player;

	if (nWorld)
		physicsSDK->releaseScene(*nWorld);
	if (physicsSDK)
		physicsSDK->release();
}

class myNxPhysx
{
public: 
	myNxPhysx(SceneManager *sceneManager) {
		initializePhysx(sceneManager);
		//std::vector<PhysicsObject*> m;
	}
	~myNxPhysx() {
		destroyPhysx();
	}

	void createTerrainActor(ET::TerrainManager *terrainManager)
	{
		createPhysxTerrain(terrainManager);
	}

	void Update(Ogre::Real time)
	{
		updatePhysx(time);
	}

};

